package com.huitone.smspfm.redis.pipeline;

import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;

import com.huitone.smspfm.redis.BaseAbstractOpts;
import com.huitone.smspfm.redis.model.AppCanstant;


/**   
* @Title PipelineOpts.java 
* @Package com.huitone.smspfm.redis.pipeline 
* @Description TODO(用一句话描述该文件做什么) 
* @author ybliang   
* @date 2017年6月10日 下午11:34:55 
* @version V1.0   
*/
public class PipelineOpts extends BaseAbstractOpts<String, String> {
	
	private static final Logger logger = LogManager.getLogger(PipelineOpts.class);

	public boolean excute(ExcuteCommands commands) {
		
		long curTryTimes = 0;
		
		boolean excuteSucc = false;
		
		do {
			try {
				curTryTimes ++;
				
				redisTemplate.executePipelined(new RedisCallback<Object>() {

					@Override
					public Object doInRedis(RedisConnection connection) throws DataAccessException {
						
						connection.openPipeline();
						commands.excute(connection);
						commands.excuteCallback(connection.closePipeline());
						
						return null;
					}
				});
				excuteSucc = true;
			} catch (Exception e) {
				
				if (e instanceof RedisConnectionFailureException) {
					excuteSucc = false;
					logger.error("从JedisPool获取连接失败,尝试获取连接:" + curTryTimes + "次! RedisServer:" + getMasterAddressPort());
				} else if (e instanceof SocketTimeoutException) {
					excuteSucc = true;
					logger.error("SoketTimeout,命令已经发送到Redis上执行,但是在等待执行结果的时候SocketRead超时! RedisServer:" + getMasterAddressPort(), e);
				} else {
					excuteSucc = false;
					logger.error("RedisServer:" + getMasterAddressPort(), e);
				}
			}
			
			if (!excuteSucc && (curTryTimes >= AppCanstant.MAXTRYTIMES)) {
				StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
				if (stackTrace != null) {
					StringBuffer buffer = new StringBuffer();
					for (StackTraceElement element:stackTrace) {
						buffer.append(element.getClassName() + "." + element.getMethodName() + " " + element.getLineNumber());
						buffer.append("\n");
					}
					logger.error("连续执行三次失败:\n" + buffer);
				}
				commands.excuteFailCallback();
			}
			
		} while (!excuteSucc && (curTryTimes < AppCanstant.MAXTRYTIMES));
		
		return excuteSucc;
	}
	
	public boolean save(ABExcuteCommands excuteCommands) {
		
		long curTryTimes = 0;
		
		boolean excuteSucc = false;
		
		do {
			try {
				curTryTimes ++;
				
				redisTemplate.executePipelined(new RedisCallback<Object>() {

					@Override
					public Object doInRedis(RedisConnection connection) throws DataAccessException {
						
						connection.openPipeline();
						excuteCommands.excute(connection);
						excuteCommands.excuteCallback(connection.closePipeline());
						
						return null;
					}
				});
				
				excuteSucc = true;
			} catch (Exception e) {
				if (e instanceof RedisConnectionFailureException) {
					excuteSucc = false;
					logger.error("从JedisPool获取连接失败,尝试获取连接:" + curTryTimes + "次! RedisServer:" + getMasterAddressPort());
				} else if (e instanceof SocketTimeoutException) {
					excuteSucc = true;
					logger.error("SoketTimeout,命令已经发送到Redis上执行,但是在等待执行结果的时候SocketRead超时! RedisServer:" + getMasterAddressPort(), e);
				} else {
					excuteSucc = false;
					logger.error("RedisServer:" + getMasterAddressPort(), e);
				}
			}
			
			if (!excuteSucc && (curTryTimes >= AppCanstant.MAXTRYTIMES)) {
				StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
				if (stackTrace != null) {
					StringBuffer buffer = new StringBuffer();
					for (StackTraceElement element:stackTrace) {
						buffer.append(element.getClassName() + "." + element.getMethodName() + " " + element.getLineNumber());
						buffer.append("\n");
					}
					logger.error("连续执行三次失败:\n" + buffer);
				}
				excuteCommands.excuteFailCallback();
			}
		
		} while (!excuteSucc && (curTryTimes < AppCanstant.MAXTRYTIMES));
		
		return excuteSucc;
	}
	
	public List<Object> get(ABExcuteCommands excuteCommands) {
		
		List<Object> results = new ArrayList<>();
		
		long curTryTimes = 0;
		
		boolean excuteSucc = false;
		
		do {
			try {
				curTryTimes ++;
				redisTemplate.executePipelined(new RedisCallback<List<Object>>() {

					@Override
					public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
						
						connection.openPipeline();
						excuteCommands.excute(connection);
						results.addAll(connection.closePipeline());
						
						return null;
					}
				});
				excuteSucc = true;
			} catch (Exception e) {
				if (e instanceof RedisConnectionFailureException) {
					excuteSucc = false;
					logger.error("从JedisPool获取连接失败,尝试获取连接:" + curTryTimes + "次! RedisServer:" + getMasterAddressPort());
				} else if (e instanceof SocketTimeoutException) {
					excuteSucc = true;
					logger.error("SoketTimeout,命令已经发送到Redis上执行,但是在等待执行结果的时候SocketRead超时! RedisServer:" + getMasterAddressPort(), e);
				} else {
					excuteSucc = false;
					logger.error("RedisServer:" + getMasterAddressPort(), e);
				}
			}
			
			if (!excuteSucc && (curTryTimes >= AppCanstant.MAXTRYTIMES)) {
				StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
				if (stackTrace != null) {
					StringBuffer buffer = new StringBuffer();
					for (StackTraceElement element:stackTrace) {
						buffer.append(element.getClassName() + "." + element.getMethodName() + " " + element.getLineNumber());
						buffer.append("\n");
					}
					logger.error("连续执行三次失败:\n" + buffer);
				}
				excuteCommands.excuteFailCallback();
			}
			
		} while (!excuteSucc && (curTryTimes < AppCanstant.MAXTRYTIMES));
		
		return results;
	}
	
	public static interface ExcuteCommands {
		void excute(RedisConnection connection);
		void excuteCallback(List<Object> results);
		void excuteFailCallback();
	}
	
	public static abstract class ABExcuteCommands implements ExcuteCommands {

		@Override
		public void excute(RedisConnection connection) {
		}

		@Override
		public void excuteCallback(List<Object> results) {
		}

		@Override
		public void excuteFailCallback() {
			
		}
		
		
	}
}
